package com.laxcen.facesdk.facelib.core;

import java.nio.Buffer;
import java.nio.ByteBuffer;
import android.graphics.Bitmap;

import com.laxcen.facesdk.facelib.core.model.FaceAttributionInfo;
import com.laxcen.facesdk.facelib.core.model.FaceLandmark3dInfo;
import com.laxcen.facesdk.facelib.core.model.FaceLandmarkPoint;
import com.laxcen.facesdk.facelib.core.model.FaceLandmarkInfo;
import java.util.ArrayList;
import com.laxcen.facesdk.facelib.core.model.FaceDetectInfo;
import java.util.List;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.SharedPreferences;
import android.os.Build;
import android.text.TextUtils;
import com.laxcen.facesdk.facelib.core.utils.DeviceUtil;
import com.tenginekit.jni.FaceSchedule;

import android.content.Context;

public class FaceCheckManager
{
    private static Context Context;
    private static String UUID;
    private static String PACKAGENAME;
    private static boolean inited;
    
    public static synchronized void init(final Context context, final AndroidConfig config) {
        if (FaceCheckManager.inited) {
            return;
        }
        if (context == null) {
            throw new RuntimeException("Context is NULL");
        }
        FaceCheckManager.Context = context;
        getID();
        if (config.openFuncList != null) {
            for (int i = 0; i < config.openFuncList.size(); ++i) {
                final String funcName = config.openFuncList.get(i).toString();
                FaceSchedule.setFunc(funcName);
            }
        }
        FaceSchedule.init(context.getAssets(), config.inputImageW, config.inputImageH, config.outputImageW, config.outputImageH, config.imageFormat.toString(), String.valueOf(config.handleMode), FaceCheckManager.PACKAGENAME, FaceCheckManager.UUID);
        FaceCheckManager.inited = true;
    }

    public static synchronized void release() {
        if (!FaceCheckManager.inited) {
            return;
        }
       FaceSchedule.release();
        FaceCheckManager.inited = false;
    }

    public static synchronized FaceDetect detect(final byte[] imageData) {
        final float[] faces = FaceSchedule.detect(imageData);
        return new FaceDetect(faces);
    }

    private static void getID() {
        try {
            String readDeviceID = DeviceUtil.readDeviceID(FaceCheckManager.Context);
            final SharedPreferences mShare = FaceCheckManager.Context.getSharedPreferences("uuid", 0);
            final String string = mShare.getString("uuid", "");
            if (string != null && readDeviceID == null && !TextUtils.equals((CharSequence)string, (CharSequence)readDeviceID) && TextUtils.isEmpty((CharSequence)readDeviceID) && !TextUtils.isEmpty((CharSequence)string)) {
                readDeviceID = string;
                DeviceUtil.saveDeviceID(readDeviceID, FaceCheckManager.Context);
            }
            if (readDeviceID == null) {
                readDeviceID = DeviceUtil.getDeviceId(FaceCheckManager.Context);
            }
            mShare.edit().putString("uuid", readDeviceID).apply();
            final PackageManager packageManager = FaceCheckManager.Context.getPackageManager();
            int flag = 8192;
            if (Build.VERSION.SDK_INT >= 24) {
                flag = 8192;
            }
            String _packageName = "";
            try {
                final PackageManager packageManager2 = FaceCheckManager.Context.getPackageManager();
                final PackageInfo packageInfo = packageManager2.getPackageInfo(FaceCheckManager.Context.getPackageName(), 0);
                _packageName = packageInfo.packageName;
            }
            catch (Exception e) {
                e.printStackTrace();
            }
            FaceCheckManager.UUID = readDeviceID;
            FaceCheckManager.PACKAGENAME = _packageName;
        }
        catch (Exception e2) {
            FaceCheckManager.UUID = "unknow";
            FaceCheckManager.PACKAGENAME = "unknow";
            e2.printStackTrace();
        }
    }

    static {
        FaceCheckManager.UUID = "unknow";
        FaceCheckManager.PACKAGENAME = "unknow";
        FaceCheckManager.inited = false;
    }

    public static class FaceDetect
    {
        private static FaceDetect Last;
        private int faceCount;
        private List<FaceDetectInfo> detectInfos;

        private FaceDetect(final float[] faces) {
            this.faceCount = 0;
            if (faces == null) {
                this.faceCount = 0;
                this.detectInfos = null;
                return;
            }
            this.faceCount = faces.length / 4;
            this.detectInfos = new ArrayList<FaceDetectInfo>(this.faceCount);
            for (int i = 0; i < faces.length; i += 4) {
                final FaceDetectInfo faceInfo1 = new FaceDetectInfo();
                faceInfo1.left = (int)faces[i];
                faceInfo1.top = (int)faces[i + 1];
                faceInfo1.right = (int)faces[i + 2];
                faceInfo1.bottom = (int)faces[i + 3];
                faceInfo1.width = faceInfo1.right - faceInfo1.left;
                faceInfo1.height = faceInfo1.bottom - faceInfo1.top;
                this.detectInfos.add(faceInfo1);
            }
            FaceDetect.Last = this;
        }

        public synchronized boolean isVaild() {
            return this == FaceDetect.Last;
        }

        public synchronized List<FaceDetectInfo> getDetectInfos() {
            return this.detectInfos;
        }

        public synchronized int getFaceCount() {
            return this.faceCount;
        }

        public synchronized List<FaceLandmarkInfo> landmark2d() {
            if (!FaceCheckManager.inited) {
                throw new RuntimeException("SDK not init");
            }
            if (!this.isVaild()) {
                throw new RuntimeException("Current obj is invaild");
            }
            if (this.faceCount == 0) {
                return null;
            }
            final List<FaceLandmarkInfo> faceLandmarkInfos = new ArrayList<FaceLandmarkInfo>(this.faceCount);
            final float[] landmarks = FaceSchedule.getLandmarks();
            final int DataCount = landmarks.length / this.faceCount;
            final int landmarkCount = (DataCount - 7) / 2;
            for (int i = 0; i < this.faceCount; ++i) {
                final FaceLandmarkInfo cur_Face = new FaceLandmarkInfo();
                cur_Face.pitch = landmarks[DataCount * i + landmarkCount * 2];
                cur_Face.yaw = landmarks[DataCount * i + landmarkCount * 2 + 1];
                cur_Face.roll = landmarks[DataCount * i + landmarkCount * 2 + 2];
                cur_Face.leftEyeClose = landmarks[DataCount * i + landmarkCount * 2 + 3];
                cur_Face.rightEyeClose = landmarks[DataCount * i + landmarkCount * 2 + 4];
                cur_Face.mouseClose = landmarks[DataCount * i + landmarkCount * 2 + 5];
                cur_Face.mouseOpenBig = landmarks[DataCount * i + landmarkCount * 2 + 6];
                cur_Face.landmarks = new ArrayList<FaceLandmarkPoint>();
                for (int j = 0; j < landmarkCount; ++j) {
                    final FaceLandmarkPoint l = new FaceLandmarkPoint(landmarks[j * 2 + DataCount * i], landmarks[j * 2 + 1 + DataCount * i]);
                    cur_Face.landmarks.add(l);
                }
                faceLandmarkInfos.add(cur_Face);
            }
            return faceLandmarkInfos;
        }

        public synchronized List<FaceLandmark3dInfo> landmark3d() {
            if (!FaceCheckManager.inited) {
                throw new RuntimeException("SDK not init");
            }
            if (!this.isVaild()) {
                throw new RuntimeException("Current obj is invaild");
            }
            if (this.faceCount == 0) {
                return null;
            }
            final List<FaceLandmark3dInfo> faceLandmarkInfos = new ArrayList<FaceLandmark3dInfo>(this.faceCount);
            final float[] landmarks = FaceSchedule.getLandmark3d();
            final int DataCount = landmarks.length / this.faceCount;
            final int landmarkCount = DataCount / 2;
            for (int i = 0; i < this.faceCount; ++i) {
                final FaceLandmark3dInfo cur_Face = new FaceLandmark3dInfo();
                cur_Face.landmarks = new ArrayList<FaceLandmarkPoint>();
                for (int j = 0; j < landmarkCount; ++j) {
                    final FaceLandmarkPoint l = new FaceLandmarkPoint(landmarks[j * 2 + DataCount * i], landmarks[j * 2 + 1 + DataCount * i]);
                    cur_Face.landmarks.add(l);
                }
                faceLandmarkInfos.add(cur_Face);
            }
            return faceLandmarkInfos;
        }

        public synchronized List<FaceAttributionInfo> attribution() {
            if (!FaceCheckManager.inited) {
                throw new RuntimeException("SDK not init");
            }
            if (!this.isVaild()) {
                throw new RuntimeException("Current obj is invaild");
            }
            if (this.faceCount == 0) {
                return null;
            }
            final List<FaceAttributionInfo> faceAttributionInfos = new ArrayList<FaceAttributionInfo>(this.faceCount);
            final float[] attributes = FaceSchedule.attritube();
            for (int size = attributes.length / 6, i = 0; i < size; ++i) {
                final FaceAttributionInfo ai = new FaceAttributionInfo();
                ai.age = (int)attributes[i * 6];
                ai.isMan = ((int)attributes[i * 6 + 1] == 0);
                ai.smile = (int)attributes[i * 6 + 2];
                ai.glasses = ((int)attributes[i * 6 + 3] > 0);
                ai.beatyOfManLook = (int)attributes[i * 6 + 4];
                ai.beautyOfWomanLook = (int)attributes[i * 6 + 5];
                faceAttributionInfos.add(ai);
            }
            return faceAttributionInfos;
        }
    }

    public static class Camera
    {
        public static synchronized void setRotation(final int ori, final boolean isScreenRotate, final int outputW, final int outputH) {
            if (!FaceCheckManager.inited) {
                return;
            }
           FaceSchedule.setOri(ori, isScreenRotate, outputW, outputH);
        }

        public static synchronized void switchCamera(final boolean back) {
            if (!FaceCheckManager.inited) {
                return;
            }
            if (back) {
               FaceSchedule.switchCamera(1);
            }
            else {
               FaceSchedule.switchCamera(0);
            }
        }
    }

    public static class Image
    {
        private static AndroidConfig.ImageFormat _InputImageFormat;
        private static AndroidConfig.ImageFormat _OutputImageFormat;

        private static synchronized void createHandler(final AndroidConfig.ImageFormat imageInputFormat, final AndroidConfig.ImageFormat imageOutputFormat) {
           FaceSchedule.createHandler(imageInputFormat.toString(), imageOutputFormat.toString());
        }

        public static synchronized Bitmap convertImage(final Bitmap bitmap, final int inputX1, final int inputY1, final int inputX2, final int inputY2, final int outputW, final int outputH, final int rotation, final boolean mirror) {
            if (bitmap == null) {
                return null;
            }
            final int imageWidth = bitmap.getWidth();
            final int imageHeight = bitmap.getHeight();
            final byte[] imageDate = getPixelsRGBA(bitmap);
            final byte[] result = convertImage(imageDate, AndroidConfig.ImageFormat.RGBA, AndroidConfig.ImageFormat.RGBA, imageWidth, imageHeight, inputX1, inputY1, inputX2, inputY2, outputW, outputH, rotation, mirror);
            final Bitmap OutputBitmap = setPixelsRGBA(result, outputW, outputH);
            return OutputBitmap;
        }

        public static synchronized byte[] convertCameraYUVData(final byte[] data, final AndroidConfig.ImageFormat imageOutputFormat, final int inputW, final int inputH, final int outputW, final int outputH, final int rotation, final boolean mirror) {
            return convertImage(data, AndroidConfig.ImageFormat.YUV_NV21, imageOutputFormat, inputW, inputH, 0, 0, inputW, inputH, outputW, outputH, rotation, mirror);
        }

        public static synchronized Bitmap convertCameraYUVData(final byte[] data, final int inputW, final int inputH, final int outputW, final int outputH, final int rotation, final boolean mirror) {
            final byte[] result = convertImage(data, AndroidConfig.ImageFormat.YUV_NV21, AndroidConfig.ImageFormat.RGBA, inputW, inputH, 0, 0, inputW, inputH, outputW, outputH, rotation, mirror);
            if (result == null) {
                return null;
            }
            final Bitmap OutputBitmap = setPixelsRGBA(result, outputW, outputH);
            return OutputBitmap;
        }

        public static synchronized byte[] convertImage(final byte[] data, final AndroidConfig.ImageFormat imageInputFormat, final AndroidConfig.ImageFormat imageOutputFormat, final int inputW, final int inputH, final int inputX1, final int inputY1, final int inputX2, final int inputY2, final int outputW, final int outputH, final int rotation, final boolean mirror) {
            if (!FaceCheckManager.inited) {
                throw new RuntimeException("SDK not init");
            }
            if (data == null || data.length == 0) {
                return null;
            }
            if (inputW == 0 || inputH == 0) {
                return null;
            }
            if (outputW == 0 || outputH == 0) {
                return null;
            }
            if (inputX1 >= inputX2 || inputY1 >= inputY2) {
                return null;
            }
            if (Image._InputImageFormat != imageInputFormat || Image._OutputImageFormat != imageOutputFormat) {
                Image._InputImageFormat = imageInputFormat;
                Image._OutputImageFormat = imageOutputFormat;
                createHandler(Image._InputImageFormat, Image._OutputImageFormat);
            }
            final byte[] result = FaceSchedule.handleImage(data, inputW, inputH, inputX1, inputY1, inputX2, inputY2, outputW, outputH, rotation, mirror);
            return result;
        }
        
        private static byte[] getPixelsRGBA(final Bitmap image) {
            final int bytes = image.getByteCount();
            final ByteBuffer buffer = ByteBuffer.allocate(bytes);
            image.copyPixelsToBuffer((Buffer)buffer);
            final byte[] temp = buffer.array();
            return temp;
        }
        
        private static Bitmap setPixelsRGBA(final byte[] byteArray, final int ImageW, final int ImageH) {
            final Bitmap image1 = Bitmap.createBitmap(ImageW, ImageH, Bitmap.Config.ARGB_8888);
            final ByteBuffer buffer = ByteBuffer.wrap(byteArray);
            buffer.get(byteArray);
            final Buffer temp = buffer.rewind();
            image1.copyPixelsFromBuffer(temp);
            return image1;
        }
        
        static {
            Image._InputImageFormat = null;
            Image._OutputImageFormat = null;
        }
    }
}
